#define _USE_MATH_DEFINES
#include <GLTools.h>
#include "ObjLoader.h"
#include "loadTGA.h"
#include <math.h>
#include <iostream>
#include <GL/freeglut.h>

#define CAMERA_PRECISION 0.1
#define MOVE_PRECISION 0.15
#define ANIMATION_SPEED 0.15
#define WHEEL_ROTATION 10
#define FLOAT_HEIGHT 1.5
#define EXIT_SUCCESS 0
#define WHEELS 4

float rotation = 0.0;
double up_down = 0.0;
double left_right = 0.0;
vector<ObjLoader*> models;
vector<Vec3f*> wheels;

//Car location vars
double deltaMove, deltaMoveY;
double carX, carY, carZ = 10;
//Camera
double xpos = 0, ypos = 0, zpos = 0, xrot = 0, yrot = 0, angle = 0;
double lastx, lasty;
GLfloat light0Pos[] = { 10, 10, 10, 1 };
GLfloat light0Color[] = { 1, 1, 1, 1 };
GLfloat light1Color[] = { 0.3f, 0.3f, 5.0f, 1 };
GLfloat light1Ambient[] = { 0, 0, 0, 1 };
GLfloat light2Color[] = { 5.0f, 0.3f, 0.3f, 1 };
GLfloat light2Ambient[] = { 0, 0, 0, 1 };
GLfloat lightSpecular[] = { 1, 1, 1, 1 };

void drawArea();
void drawObjects();
void drawModels();
void camera();
void pressKey(int key, int x, int y);
void releaseKey(int key, int x, int y);

void display(void)
{
	glClearColor(1.0, 1.0, 1.0, 1.0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();
	camera();
	drawArea();
	drawModels();
	drawObjects();
	glutSwapBuffers();
}

void drawArea()
{
	/* The 'floor'. */
	glBegin(GL_TRIANGLES);
	glColor3f(0.0f, 0.0f, 0.0f);
	glVertex3f(100.0f, 0.0f, 100.0f);
	glVertex3f(100.0f, 0.0f, -100.0f);
	glVertex3f(-100.0f, 0.0f, -100.0f);
	glVertex3f(-100.0f, 0.0f, -100.0f);
	glVertex3f(100.0f, 0.0f, 100.0f);
	glVertex3f(-100.0f, 0.0f, 100.0f);
	glEnd();
}

void drawObjects()
{
	glPushMatrix();
	glTranslated(left_right, 2, up_down);
	GLfloat light1Pos[] = { 0, 0, 0, 1 };
	glLightfv(GL_LIGHT1, GL_POSITION, light1Pos);
	glDisable(GL_LIGHTING);
	glColor3f(0.3f, 0.3f, 1.0f);
	glutSolidSphere(.35, 12, 12);
	glEnable(GL_LIGHTING);
	glPopMatrix();
	glPushMatrix();
	glTranslated(0, 5, 0);
	glutSolidDodecahedron();
	glPopMatrix();
}

void drawModels()
{
	glPushMatrix();
	glTranslated(left_right, 0, up_down);
	models[0]->draw();
	for(unsigned int i = 0; i < wheels.size(); i++)
	{
		glPushMatrix();
		if(!(i % 2))
		{
			glRotated(180, 0, 0, 1);
			glTranslated(wheels[i]->x + 1.4, -.35, wheels[i]->z);
			glRotated(rotation, 1, 0, 0);
		}
		else
		{
			glTranslated(wheels[i]->x, .35, wheels[i]->z);
			glRotated(-rotation, 1, 0, 0);
		}
		models[1]->draw();
		glPopMatrix();
	}
	glPopMatrix();
	glPushMatrix();
	glPushMatrix();
	glTranslated(carX, FLOAT_HEIGHT + 2, carZ);
	GLfloat light2Pos[] = { 0, 0, 0, 1 };
	glLightfv(GL_LIGHT2, GL_POSITION, light2Pos);
	glDisable(GL_LIGHTING);
	glColor3f(1.0f, 0.3f, 0.3f);
	glutSolidSphere(.35, 12, 12);
	glEnable(GL_LIGHTING);
	glPopMatrix();
	glTranslated(carX, FLOAT_HEIGHT, carZ);
	glRotated(carY, 0, 1, 0);
	if(carY == -90)
		carX += ANIMATION_SPEED;
	else
		carX -= ANIMATION_SPEED;
	if(carX > 20)
		carY = 90;
	else if (carX < 0)
		carY = -90;
	models[2]->draw();
	glPopMatrix();
}

void reshape(GLint width, GLint height)
{
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(90, width / (float)height, 1, 1000);
	glMatrixMode(GL_MODELVIEW);
}

void initGraphics(void)
{
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_TEXTURE_2D);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glEnable(GL_LIGHT1);
	glEnable(GL_LIGHT2);
	glEnable(GL_SMOOTH);
	glEnable(GL_BLEND);
	models.push_back(new ObjLoader("Models/bug_car.obj"));
	models.push_back(new ObjLoader("Models/bug_wheel.obj"));
	models.push_back(new ObjLoader("Models/bug_car.obj"));
	wheels.push_back(new Vec3f(-.7f, 0, -.7f));
	wheels.push_back(new Vec3f(.7f, 0, .7f));
	wheels.push_back(new Vec3f(-.7f, 0, .7f));
	wheels.push_back(new Vec3f(.7f, 0, -.7f));
	glLightfv(GL_LIGHT0, GL_POSITION, light0Pos);
	glLightfv(GL_LIGHT0, GL_AMBIENT, light0Color);
	glLightfv(GL_LIGHT0, GL_SPECULAR, lightSpecular);
	glLightfv(GL_LIGHT1, GL_DIFFUSE, light1Color);
	//glLightfv(GL_LIGHT1, GL_AMBIENT, light1Ambient);
	glLightfv(GL_LIGHT2, GL_DIFFUSE, light2Color);
	//glLightfv(GL_LIGHT2, GL_AMBIENT, light2Ambient);
	GLfloat ambient_model[] = { 1.0, 1.0, 1.0, 1.0 };
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient_model);
}

void mouseButton(int button, int state, int x, int y)
{

}

void mouseMotion(int x, int y)
{

}

void passiveMouseMotion(int x, int y)
{
	double diffx = x-lastx;
	double diffy = y-lasty;
	lastx = x;
	lasty = y;
	xrot += diffy;
	yrot += diffx;
}

void pressKey(int key, int x, int y) 
{
	switch(key)
	{
	case GLUT_KEY_LEFT:
		left_right -= MOVE_PRECISION;
		break;
	case GLUT_KEY_RIGHT:
		left_right += MOVE_PRECISION;
		break;
	case GLUT_KEY_UP:
		rotation += WHEEL_ROTATION;
		up_down -= MOVE_PRECISION;
		break;
	case GLUT_KEY_DOWN:
		rotation -= WHEEL_ROTATION;
		up_down += MOVE_PRECISION;
		break;
	}
}

void releaseKey(int key, int x, int y) 
{ 	
	switch(key)
	{
	case GLUT_KEY_LEFT:
	case GLUT_KEY_RIGHT:
		deltaMove = 0;
		break;
	case GLUT_KEY_UP:
	case GLUT_KEY_DOWN:
		deltaMoveY = 0;
		break;
	}
}

void idleFunc(void)
{
	//rotation += 2;
	glutPostRedisplay();
}

void keyboard(unsigned char key, int x, int y)
{
	double xrotrad, yrotrad;
	switch(key)
	{
	case 27:
		exit(EXIT_SUCCESS);				/*Quit application. */
		break;
	case 65:
	case 97:
		yrotrad = yrot / 180 * M_PI;
		xpos -= cos(yrotrad) * CAMERA_PRECISION;
		zpos -= sin(yrotrad) * CAMERA_PRECISION;
		break;
	case 68:
	case 100:
		yrotrad = yrot / 180 * M_PI;
		xpos += cos(yrotrad) * CAMERA_PRECISION;
		zpos += sin(yrotrad) * CAMERA_PRECISION;
		break;
	case 83:
	case 115:
		yrotrad = yrot / 180 * M_PI;
		xrotrad = xrot / 180 * M_PI; 
		xpos -= sin(yrotrad);
		zpos += cos(yrotrad);
		ypos += sin(xrotrad);
		break;
	case 87:
	case 119:
		yrotrad = yrot / 180 * M_PI;
		xrotrad = xrot / 180 * M_PI; 
		xpos += sin(yrotrad);
		zpos -= cos(yrotrad);
		ypos -= sin(xrotrad);
		break;
	default:
		break;
	}
}

void camera (void)
{
	if(ypos < 0)
		ypos = 0;
	glRotated(xrot, 1, 0, 0);
	glRotated(yrot, 0, 1, 0);
	glTranslated(-xpos, -ypos - 3, -zpos);
}

int main(int argc, char* argv[])
{
	glutInit(&argc, argv);
	glutInitWindowSize(1600, 900);
	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
	glutCreateWindow("OpenGL Opdrachten");
	initGraphics();
	//glutFullScreen();
	/*Register callbacks. */
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutKeyboardFunc(keyboard);
	glutMouseFunc(mouseButton);
	glutMotionFunc(mouseMotion);
	glutPassiveMotionFunc(passiveMouseMotion);
	glutIdleFunc(idleFunc);
	glutSpecialFunc(pressKey);
	glutSpecialUpFunc(releaseKey);
	/*Turn the flow of control over to GLUT. */
	glutMainLoop();
	return EXIT_SUCCESS;
}